import { isObject, def } from "../util/index";
import { arrayMethods } from "../util/array";
import Dep from "./dep";

// 把data中的数据，都通过Object.defineProperty重新定义
export function observe(data) {
  if (!isObject(data)) {
    return;
  }
  return new Observer(data);
}

class Observer {
  constructor(value) {
    this.dep = new Dep(); //该dep是为了给数组添加依赖收集
    // value.__ob__ = this; //死循环

    // Object.defineProperty(value, "__ob__", {
    //   enumerable: false,
    //   configurable: false,
    //   value: this,
    // });

    //   给监控过的对象增加__ob__属性，供后边处理数组对象时调用对象上的方法
    def(value, "__ob__", this);

    if (Array.isArray(value)) {
      // 如果value是数组，单独处理
      // 数组原型方法的劫持 push shift pop unshift splice
      value.__proto__ = arrayMethods;
      // 如果数组里面放的是对象，再次进行对象监测
      this.observerArray(value);
    } else {
      // 处理data数据层级过多，需要递归解析对象中的属性，依次增加get set方法
      this.walk(value);
    }
  }

  observerArray(value) {
    value.forEach((v) => {
      observe(v);
    });
  }

  walk(data) {
    /* 使用for循环
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
      let key = keys[i];
      let value = data[key];
      // 添加响应式方法
      defineReactive(data, key, value);
    } */
    Object.keys(data).forEach((key) => {
      defineReactive(data, key, data[key]);
    });
  }
}

function defineReactive(data, key, value) {
  let dep = new Dep();
  let childObj = observe(value); //childObj 是数组的observer实例

  Object.defineProperty(data, key, {
    configurable: true,
    enumerable: true,
    get() {
      if (Dep.target) {
        //如果当前有watcher
        // depend添加依赖
        dep.depend();
        if (childObj) {
          // *******数组的依赖收集********
          childObj.dep.depend();
          // 如果数组中还是数组
          if (Array.isArray(value)) {
            // 把内部数组的每一个数组都进行依赖收集
            dependArray(value);
          }
        }
      }
      return value;
    },
    set(newVal) {
      if (newVal === value) {
        return;
      }
      // console.log("value change,这里可以做依赖收集");
      observe(newVal); //劫持用户设置的新值，该值有可能为对象
      value = newVal;
      dep.notify(); //通知依赖的watcher进行更新
    },
  });
}

function dependArray(value) {
  for (let i = 0; i < value.length; i++) {
    let current = value[i]; // 将数组中的每一个元素都取出来，数据变化后也更新视图
    current.__ob__ && current.__ob__.dep.depend();
    if (Array.isArray(current)) { //数组内的元素类型还是数组，进行深度遍历
      dependArray(current);
    }
  }
}

// 在模板取值时，进行getter操作并触发了depend依赖的收集，在更新数据时触发setter的操作，更新对应的watcher，从而更新页面。
// dep和watcher是多对多的关系
